<script setup lang="ts">
  import { computed } from 'vue';
  import { isArray } from '@/utils/is';

  /* status:'loading','error','success','empty' */
  type ViewStatus = 'loading' | 'error' | 'success' | 'empty';

  interface SkeletonProps<T = any> {
    // 当前视图状态
    status: ViewStatus;
    // success时传递的数据
    data: T;
    // loading时占位数据，撑起骨架
    placeholderResult?: T;
    // 空数据时的文案
    emptyMsg?: string;
    // 错误时的文案
    errorMsg?: string;
    // 具体判空回调
    isEmpty?: (data: T) => boolean;
    // 不使用placeholderResult时,在原视图上覆盖 loading 的方式展示过渡效果，默认false
    coverLoading?: boolean;
    // 空内容时展示数据
    showViewInEmpty?: boolean;
  }

  const props = withDefaults(defineProps<SkeletonProps>(), {
    status: 'loading',
    emptyMsg: '暂无数据',
    errorMsg: '未知错误',
    coverLoading: false,
    showViewInEmpty: false,
  });

  const emits = defineEmits(['retry']);

  const retryClick = () => {
    emits('retry');
  };

  const viewStatus = computed(() => {
    const { status, data } = props;

    if (status === 'success') {
      let isEmp = false;
      if (props.isEmpty) {
        isEmp = props.isEmpty(props.data);
      } else if (isArray(data)) {
        isEmp = data.length === 0;
      } else if (!data) {
        isEmp = true;
      } else {
        isEmp = false;
      }

      if (isEmp) {
        return 'empty';
      }

      return 'success';
    }
    return status;
  });

  const placeholderData = computed(() => {
    if (props.data) {
      return props.data;
    }
    return props.placeholderResult;
  });
</script>

<template>
  <div
    v-if="viewStatus === 'empty' && !showViewInEmpty"
    key="empty"
    class="empty_view"
  >
    <a-empty>
      <template #image>
        <icon-empty size="100" />
      </template>
      <span class="text-xl">{{ emptyMsg }}</span>
    </a-empty>
  </div>

  <div v-else-if="viewStatus === 'error'" key="error" class="empty_view">
    <a-empty>
      <template #image>
        <icon-exclamation-circle-fill size="100" />
      </template>
      <span class="text-xl">{{ errorMsg }}</span>
    </a-empty>
    <a-button class="mt4 max-w-160px" @click="retryClick">重试</a-button>
  </div>

  <div
    v-else
    key="loadingOrContent"
    :class="[
      placeholderData && viewStatus === 'loading'
        ? 'skeleton-view-empty-view'
        : 'skeleton-view-default-view',
    ]"
  >
    <!-- 展示loading图标，只有在不是coverLoading的模式，并且placeholderData为空，并且是loading状态下 -->
    <div
      v-if="!coverLoading && !placeholderData && viewStatus === 'loading'"
      class="loading-center"
    >
      <icon-loading size="40" />
    </div>
    <!-- 展示 spin，在状态为loading时，且placeholderData为空时 -->
    <a-spin
      v-else
      :spinning="viewStatus === 'loading' && !placeholderData"
      class="w-full h-full"
    >
      <slot
        :data="placeholderData"
        :status="viewStatus"
        :success="viewStatus === 'success'"
        :mask="viewStatus === 'loading' ? 'skeleton-mask' : ''"
      ></slot>
    </a-spin>
  </div>
</template>

<style lang="less">
  .clam-box {
    width: 100%;
    height: 100%;
  }
  .empty_view {
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    width: 100%;
    height: 100%;
  }
  .empty_img {
    width: 310px;
    height: 218px;
  }
  .trip_text {
    font-size: 20px;
    color: #999999;
  }

  .mt4 {
    margin-top: 4px;
  }

  .flex-col {
    display: flex;
    flex-direction: column;
  }

  .loading-center {
    padding: 20px;
    display: flex;
    justify-content: center;
    align-items: center;
  }

  .skeleton-view-empty-view {
    position: relative;
    pointer-events: none;
  }

  .skeleton-view-empty-view::before {
    content: ' ';
    position: absolute;
    width: 100%;
    height: 100%;
    top: 0;
    left: 0;
    background: linear-gradient(
      110deg,
      rgba(255, 255, 255, 0.1) 40%,
      rgba(180, 199, 255, 0.3) 50%,
      rgba(255, 255, 255, 0.1) 60%
    );
    background-size: 200% 100%;
    background-position-x: 180%;
    animation: loading 1s ease-in-out infinite;
    z-index: 1;
  }

  @keyframes loading {
    to {
      background-position-x: -20%;
    }
  }

  .skeleton-view-empty-view .skeleton-mask {
    position: relative;
  }
  .skeleton-view-empty-view .skeleton-mask::before {
    content: ' ';
    background-color: #f5f5f5;
    position: absolute;
    width: 100%;
    height: 100%;
    border: 1px solid #f5f5f5;
    top: -1px;
    left: -1px;
    z-index: 1;
  }

  .skeleton-view-default-view {
    input,
    a,
    button,
    .arco-card-header-title,
    .arco-card-header-extra,
    .arco-card-body,
    .skeleton-cover,
    .arco-descriptions-item-value,
    .arco-descriptions-item-label {
      transition-duration: 0.7s;
      transition-timing-function: ease;
    }

    span {
      &:not(button span):not(span span):not(.arco-card span) {
        transition-duration: 0.7s;
        transition-timing-function: ease;
        transition-property: background;
      }
    }
  }

  .skeleton-view-empty-view {
    /* 字体颜色透明 */
    color: transparent !important;

    button,
    span,
    input,
    td,
    div,
    a {
      color: transparent !important;
    }

    input::placeholder {
      color: transparent !important;
    }

    canvas {
      opacity: 0;
    }

    // 背景变色构建骨架
    span {
      &:not(button span):not(span span):not(.arco-card span) {
        border: none !important;
        background: #e5e5e5 !important;
      }
    }

    input,
    a,
    button,
    .arco-card-header-title,
    .arco-card-header-extra,
    .arco-card-body,
    .skeleton-cover,
    .arco-descriptions-item-value,
    .arco-descriptions-item-label {
      border: none !important;
      background: #e5e5e5 !important;
    }
  }

  /* [src=""],img:not([src])*/
  .skeleton-view-empty-view img {
    content: url(./no_url.png);
    border-radius: 2px;
    background: #f5f5f5 !important;
  }
</style>
